<?php
/**********************************************************************************
 
 	Provides functions to handle recipes 
	
	Copyright (C) 2010 Benjamin Thomas
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 	Author: Benjamin Thomas
	Email: thomas.ben@gmail.com
 	Created: 7/31/10
 
	Inputs:
		int id				Database's id of the recipe
		MarketDB &db		Instance of MarketDB to be used, created if not provided
		
 	Calls:
		calculateCosts ( )
			Checks this recipe's tree and adds costs to items. Does not alter the tree.	
		minimizeCosts( )
			Checks this recipe's tree for costs and removes any recipes more expensive than their product.	
		loadItems ( )
			Causes Recipe's database to load all items from the database into an array.
		toArray ( )
			Returns the recipe's crafting tree	
 
 ***********************************************************************************
 */
 
require_once(dirname(__FILE__) . "/MarketDB.php");

class Recipe {
	protected $db;
	protected $inCount = 0;
	protected $outCount = 0;
	protected $product;
	protected $price;
	protected $cost;
	protected $cd;
	protected $prof;
	protected $known = false;
	protected $inputs = array();
	/*
		id		Recipe's craftid from the database
		db		MarketDB object to use, if not supplied one is created
	*/	
	public function __construct($id, &$db = null) {
		if (!$db) {
			$this->db = new MarketDB();
		} else {
			$this->db = &$db;
		}
		$this->id = $id;
		$recipe = $this->db->getRecipe($id);
		$this->product = $recipe['productID'];
		$this->price = $this->getPrice($recipe['productID']);
		$this->cd = $recipe['cd'];
		$this->outCount = $recipe['output'];
		$this->prof = $recipe['profID'];
		if ($this->prof != 6) {
			$sql = "SELECT craftid FROM craftingskills as s LEFT JOIN chars ON chars.charid = s.charid WHERE craftid = $id AND server = " . $this->db->getServerId();
			$this->known = (bool) $this->db->publicQuery($sql);
		} else {
			$this->known = true;
		}
		$this->inputs[0] = $this->getInputs($id, (1/$recipe['output']), array($recipe['productID']));
	}
	
	public function __destruct() {
		unset($this->db);
		unset($this->inputs);
	}
	
	/*
		calculateCosts( )
		Checks this recipe's tree and adds costs to items. Does not alter the tree.
	*/
	public function calculateCosts() {
		$this->getCosts($this->toArray());
	}
	
	/*
		minimizeCosts( )
		Checks this recipe's tree for costs and removes any recipes more expensive than their product.	
	*/
	public function minimizeCosts() {
		$this->getCosts($this->toArray(), false);
		unset($this->inputs);
	}
	
	/*
		getCosts( array &$arr, boolean $strip )
		Checks the tree recursively and adds the costs to each recipe
		Parameters:
			arr		The recipe's tree that has already been created
			strip	Remove any recipes that are more expensive than their product
		Returns:
			None
	*/
	private function getCosts(&$arr, $strip = false) {
		//echo "<pre>"; print_r($arr); echo "</pre>";
		if (is_array($arr) && array_key_exists('inputs', $arr)) {
			$min = 0;
			//Loop through each recipe
			for ($i = 0; $i < count($arr['inputs']); $i++) {
				//Loop through each input of the recipe
				$sum = 0;
				$pointer = &$arr['inputs'][$i];
				for ($j = 0; $j < count($pointer); $j++) {
					$this->getCosts($pointer[$j]);
					$sum += $pointer[$j]['price'] * ($pointer[$j]['in']/$arr['in']);
				}
				if ($sum == 0 || $sum > $min) { $min = $sum; }				
			}
			//The root level of the tree needs to have its cost changed differently
			if (!array_key_exists('recipe', $arr)) {
				$this->cost = $min;
			}
			// Ignore the cost if the item is cheaper
			if ($arr['price'] == 0 || $min < $arr['price']) {
				$arr['cost'] = $min;
			} else {
				$arr['cost'] = $arr['price'];
			}
		}
	}
	
	/*
		getInputs( int $id, int $multiplier, array $invalid ) 
		Gets recipe from database and puts it into a recursive array
		Parameters:
			id						Craft id of the recipe 
			multiplier (Optional)	Number to be multiplied onto the input count from the previous tree(s)
			invalid	(Optional)		Array of item ids already used previously in the tree (prevents endless loops)	
		Note: invalid should not be needed to be set from outside the function
	*/
	private function getInputs($id, $multiplier = 1, $invalid = array()) {
		$arr = array();
		$sql = "SELECT * FROM craftinginputs WHERE craftID = " . $id;
		$inputs = $this->db->publicQuery($sql);
		// Exit if the inputs could not be found (Database may be corrupt)
		if (!is_array($inputs) || count($inputs) == 0) { return false; }
		// Check ids against the current list to avoid infinite loops
		$curids = Recipe::getIDs($inputs, 'inputID');
		if (array_intersect($invalid, $curids)) { return false; }
		$inputcount = count($inputs);
		for ($i = 0; $i < $inputcount; $i++) {
			$arr[$i]['id'] = $inputs[$i]['inputID'];
			$arr[$i]['in'] = $inputs[$i]['inputCount'] * $multiplier;
			$arr[$i]['price'] = Recipe::getPrice($inputs[$i]['inputID']);
			//Check if the input has any associated recipes
			$sql = "SELECT craftID, cd, output, profid FROM crafting WHERE productID = " . $inputs[$i]['inputID'] . ' AND cd = 0';
			$result = $this->db->publicQuery($sql);
			if ($result && count($result) > 0) {
					//Add them to the invalid list for recursion
				$newinvalid = $invalid;
				$newinvalid[] = $inputs[$i]['inputID'];		
				//Update the multiplier for recursion
				$resultcount = count($result);
				//Loop to support multiple recipes for a single product
				for ($j = 0; $j < $resultcount; $j++) {
					if ($result[$j]['profid'] == 6) {
						$arr[$i]['known'] = true;
					} else {
						$sql = "SELECT craftid FROM craftingskills as s LEFT JOIN chars ON chars.charid = s.charid WHERE server = " . $this->db->getServerId();
						$sql .= " AND craftid = " . $result[$j]['craftID'];
						$knowncheck = $this->db->publicQuery($sql);
						if ($knowncheck) {
							$arr[$i]['known'] = true;
						} else {
							$arr[$i]['known'] = false;
						}
					}
					$arr[$i]['recipe'] = $result[$j]['craftID'];
					$arr[$i]['cd'] = $result[$j]['cd'];
					$newmultiplier = $multiplier * $inputs[$i]['inputCount'] * (1/$result[$j]['output']);
					$arr[$i]['inputs'][$j] = $this->getInputs($result[$j]['craftID'], $newmultiplier, $newinvalid);
					//This recipe returned false because it uses something on this branch of the tree
					if ($arr[$i]['inputs'][$j] === false) {
						unset($arr[$i]['inputs'][$j]);
						//Exit for loop to break this recipe
						break;
					}
				}			
			}
		}
		return $arr;
	}
	
	/*
		loadItems()
		Causes Recipe's database to load all items from the database into an array.
		(Useful for pages that are accessing large numbers of items.)
	*/
	public function loadItems() {
		$db->loadItems();
	}
	
	/*
		toArray()
		Returns array of the recipe's tree
	*/
	public function toArray() {
		$tmp = array();
		$tmp['cd'] = $this->cd;
		$tmp['out'] = $this->outCount;
		$tmp['in'] = 1;
		$tmp['id'] = $this->product;
		$tmp['price'] = $this->price;
		$tmp['inputs'] = &$this->inputs;
		$tmp['cost'] = $this->cost;
		$tmp['cid'] = $this->id;
		$tmp['prof'] = $this->prof;
		$tmp['known'] = $this->known;
		return $tmp;
	}
	
	/*
		getIDs ( array &$arr, string $key )
		Returns an array with all of the values in a 2 dimensional array with a provided key
		Parameters:
			arr 	The array
			key 	The key to be used	
	*/
	static protected function getIDs(&$arr, $key) {
		$count = count($arr);
		$result = array();
		for ($i = 0; $i < $count; $i++) {
			$result[] = $arr[$i][$key];
		}
		return $result;
	}
	
	/*
		getPrice( int $id )
		Returns the vendor price of an item or returns the moving price
		Parameters:
			id 		item id to be checked	
	*/
	protected function getPrice($id) {
		if (!array_key_exists($id, $this->db->items)) {
			$this->db->loadItems($id);
		}
		if ($this->db->items[$id]['price']) {
			return $this->db->items[$id]['price'];
		}
		if (!array_key_exists('movingprice', $this->db->items[$id])) {
			$this->db->items[$id]['movingprice'] = $this->db->getMovingPrice($id,7);
		}
		return $this->db->items[$id]['movingprice'];
	}
}